//Application.cpp

#include "Application.h"

#include <iostream>
using namespace std;

//constructor
Application::Application()
{
	m_isFullFill = true; //default value - full rectangles
	init();
}

//initializes the menus for the shapes application
void Application::init()
{
	//main menu initialize
	m_menu.init(9);
	m_menu.set(0, "Add a shape");
	if (m_isFullFill)
	{
		m_menu.set(1, "Draw full shapes");
		m_menu.set(2, "Change default to drawing empty shapes");
	}
	else
	{
		m_menu.set(1, "Draw empty shapes");
		m_menu.set(2, "Change default to drawing full shapes");
	}
	m_menu.set(3, "Choose a shapes");
	m_menu.set(4, "Display animation with flowing shapes across the screen sides");
	m_menu.set(5, "Display animation with collisions");
	m_menu.set(6, "For future use");
	m_menu.set(7, "For future use");
	m_menu.set(8, "Exit");

	//sub menu initialize
	m_subMenu.init(6);
	m_subMenu.set(0, "Return to main menu");
	m_subMenu.set(1, "Delete selected shape");
	m_subMenu.set(2, "Send selected shape to front");
	m_subMenu.set(3, "Send selected shape to background");
	m_subMenu.set(4, "Shift selected shape");
	m_subMenu.set(5, "Add animateion");

	//shape thpes menu
	m_shapeTypeMenu.init(2);
	m_shapeTypeMenu.set(0, "Rectangle");
	m_shapeTypeMenu.set(1, "Circle");
}

//adds a shape to the container (includes the input of the values and output messages)
void Application::addShape()
{
	int typeShape = 0; //1 = rectangle , 2 = circle
	Shape* shapeP = NULL;

	cout << "Select the type of shape for addition: " << endl;	
	m_shapeTypeMenu.printMenu();
	typeShape = m_shapeTypeMenu.getChoiceFromUser();
	switch (typeShape)
	{
	case 1:
		shapeP = getRectFromUser();
		break;
	case 2:
		shapeP = getCircleFromUser();
		break;
	default:
		cerr << "ERROR - wrong type of shape!" << endl;
	}
	if (shapeP == NULL)
		cerr << "Error - the shape creation didn't return a vaild address!" <<endl;
	shapeP->saveOriginalPoint();
	m_shapeCont.addShapeToBack(shapeP);
	m_shapeCont.drawAll('*', m_isFullFill);
	_getch();
	clrscr();
	m_menu.printMenu();
}

//gets a rectangle properties from the user and creates a rectangle
Rect* Application::getRectFromUser()
{
	Rect* rectP = NULL;
	int x, y;
	Point point;
	unsigned int ver, hor;

	cout << "Please enter a point - X Y: " << endl;
	cin >> x >> y;
	point.set(x, y);
	cout << "Please enter vertical and horizontal values (natural numbers only): " << endl;
	cin >> ver >> hor;
	while (ver < 0 || hor < 0)
	{
		cout << "Invalid values! Please enter only natural numbers for vertical and horizontal" << endl;
		cout << "Please enter vertical and horizontal values (natural numbers only): " << endl;
		cin >> ver >> hor;
	}
	rectP = new Rect(point, ver, hor);
	return rectP;
}

//gets a circle properties from the user and creates a circle
Circle* Application::getCircleFromUser()
{
	Circle* circleP = NULL;
	int x, y;
	Point point;
	unsigned int rad;

	cout << "Please enter a point - X Y: " << endl;
	cin >> x >> y;
	point.set(x, y);
	cout << "Please enter a radius value (natural number only): " << endl;
	cin >> rad;
	while (rad < 0)
	{
		cout << "Invalid value! please enter only natural number for the radius" << endl;
		cout << "Please enter a radius value (natural number only): " << endl;
		cin >> rad;
	}
	circleP = new Circle(point, rad);
	return circleP;
}

//changes the default type of shape: full<->empty
void Application::changeShapeFill()
{
	m_isFullFill = !m_isFullFill;
	if (m_isFullFill)
	{
		m_menu.set(1, "Draw full shapes");
		m_menu.set(2, "Change default to drawing empty shapes");
	}
	else
	{
		m_menu.set(1, "Draw empty shapes");
		m_menu.set(2, "Change default to drawing full shapes");
	}
}

//clrears the buffer of cin in stdio
void Application::clearReadBuffer()
{
	if (!cin)	//in a case of junk in cin buffer
	{
		cin.clear();	//clears cin buffer
		string ignoreLine; //read the invalid input into it
		getline(cin, ignoreLine); //read the line till next space
	}
}

//ask the user for increments values and move the shape
bool Application::moveShape(unsigned int index)
{
	int incX, incY;
	Shape* shapeP = NULL;

	cout << "Please enter increments values (positive or negative values) - X Y: " << endl;
	cin >> incX >> incY;
	shapeP = m_shapeCont.getShape(index);
	if (shapeP != NULL)
	{
		shapeP->move(incX, incY);
		shapeP->saveOriginalPoint();
		return true;
	}
	else
		return false;
}

//ask the user for shifts values and add animation to the shape
bool Application::addAnimate(unsigned int index)
{
	float shiftX, shiftY;
	Shape* shapeP = NULL;

	cout << "Please enter animation shifts values - X Y: " << endl;
	cin >> shiftX >> shiftY;
	shapeP = m_shapeCont.getShape(index);
	if (shapeP != NULL)
	{
		shapeP->setShifts(shiftY,shiftX);
		shapeP->saveShiftsToOriginal();
		return true;
	}
	else
		return false;
}

//waits for any key to be pressed and then prints the main menu
void Application::pressKeyReturnMainMenu()
{
	_getch();
	clrscr();
	m_menu.printMenu();
}

//draws all the shape in the container
void Application::drawAllShapes()
{
	m_shapeCont.drawAll('*', m_isFullFill);
	pressKeyReturnMainMenu();
}

void Application::runSubMenu(unsigned int idxSpecial)
{
	unsigned int choice = 0;

	//prints sub menu
	clrscr();
	m_subMenu.printMenu();
	choice = m_subMenu.getChoiceFromUser();
	switch (choice)
	{
		case 1: // cencel selection
			//none
			break;
		case 2: // delete the shape from the container
			if (!m_shapeCont.removeShape(idxSpecial))
				cerr << "ERROR - failed to remove the shpae from the container!" << endl;
			break;
		case 3: // bring shape to be first on the screeen (last in the container)
			if (!m_shapeCont.moveShapeToBack(idxSpecial))
				cerr << "ERROR - failed to move the shpae to the back!" << endl;
			break;
		case 4: // bring shape to be last on the screeen (fiirst in the container)
			if (!m_shapeCont.moveShapeToFront(idxSpecial))
				cerr << "ERROR - failed to move the shpae to the front!" << endl;
			break;
		case 5: // moves shpe to new point
			if (!moveShape(idxSpecial))
				cerr << "ERROR - failed to move the shpae!" << endl;
			break;
		case 6: // adds animation to the shape, shifts for x and y
			if (!addAnimate(idxSpecial))
				cerr << "ERROR - failed add animation shifts to the shape!" << endl;
			break;
		default:	
			cout << "invalid option!" << endl;
			cout << "Please enter vaild choice: ";
			break;
	}
	drawAllShapes();
}

//asks from the user for a point and marks the shape that include that point
void Application::markShape()
{
	int idxSpecial = 0; //gets the index of the selected shape or -1
	int x, y;
	Point point;

	cout << "Please enter a shape point for selection - X Y: " << endl;
	cin >> x >> y;
	point.set(x ,y);
	point.draw('@');
	idxSpecial = m_shapeCont.findShapeWithPointInside(point);
	if (idxSpecial >= 0)
	{
		m_shapeCont.drawAllWithOneSpecial('*', m_isFullFill, idxSpecial, '+');
		
	}
	else
		m_shapeCont.drawAll('@', m_isFullFill);
	point.draw('@');
	pressKeyReturnMainMenu();
	runSubMenu(idxSpecial);
}

//test function - for debuging only!
//void Application::InjectTestData()
//{
//	Point point1;
//	point1.set(45,12);
//	Point point2;
//	point2.set(18,10);
//	Rect* rect1 = new Rect(point1, 4, 4);
//	Rect* rect2 = new Rect(point2, 5, 5);
//	rect1->setShifts(0.2, -0.8);
//	rect2->setShifts(0.3, +0.5);
//	rect1->saveOriginalPoint();
//	rect2->saveOriginalPoint();
//	m_shapeCont.addShapeToBack(rect1);
//	m_shapeCont.addShapeToBack(rect2);
//
//	Point point3;
//	point3.set(5,10);
//	Point point5;
//	point5.set(6,10);
//	Circle* circ1 = new Circle(point3, 5);
//	Circle* circ3 = new Circle(point5, 2);
//	circ1->setShifts(-0.2, -0.8);
//	circ3->setShifts(0.1, 0.1);
//	circ1->saveOriginalPoint();
//	circ3->saveOriginalPoint();
//	m_shapeCont.addShapeToBack(circ1);
//	m_shapeCont.addShapeToBack(circ3);
//}

//runs the application
void Application::run()
{
	bool shouldExit = false;
	unsigned int choice = NULL;

	while (!shouldExit)
	{
		clrscr();
		m_menu.printMenu();	//prints the main menu
		clearReadBuffer();
		choice = m_menu.getChoiceFromUser();
		switch (choice)
		{
			case 1: // add shape
				addShape();
				break;
			case 2: // draw all shapes
				drawAllShapes();
				break;
			case 3: // change Default Type
				changeShapeFill();
				break;
			case 4: // mark shape and enter sub menu
				markShape();
				break;
			case 5:	//display animation with flowing across the screen sides
				m_animate.animateAcrossScr(m_shapeCont, m_isFullFill);
				break;
			case 6:	//display animation with collisions
				m_animate.animateCollisions(m_shapeCont, m_isFullFill);
				break;
			case 9: //exit
				shouldExit = true;
				break;
			default:	
				if (choice >= 7 && choice <= 8)	//for the futue options: 5-8
					cout << "This option is for future use, please choose another option!" << endl;
				else
					cout << endl << "invalid option!" << endl;
				cout << "Please enter your choice: ";
				break;
		}
	}
}